What is the difference between [:] and [::] when python replicates a list
- 2020-12-18 01:51:49
- OfStack
preface
new = old[:]
Python veterans know what this code means. It copies the list old to new. It can be confusing for beginners and should be avoided. Unfortunately [:] markup is widely used, probably because Python programmers don't know a better way to copy lists. However, this article introduces the difference between [:] and [::] when copying python lists. Let's start with 1
We can (shallowly) copy the list using [:] :
l = [1, 2, 3]
z1 = l[:]
We can also (shallowly) copy it using [::] :
z2 = [::]
Now z1 == z2 will be True. After reading the answers for Explain Python 's slice notation, I understand how these images work.
But, my question is is there a difference between the two internally? Is it more efficient to replicate than others, or do you do exactly the same thing?
The best answer
There is absolutely no difference between them, at least in Python 3. If you prefer, you can use dis.dis to check each of these bytecode uses:
l = [1, 2, 3, 4]
Bytecode issued for l [:] :
from dis import dis
dis('l[:]')
1 0 LOAD_NAME 0 (l)
3 LOAD_CONST 0 (None)
6 LOAD_CONST 0 (None)
9 BUILD_SLICE 2
12 BINARY_SUBSCR
13 RETURN_VALUE
Bytecode sent for l [::] :
dis('l[::]')
1 0 LOAD_NAME 0 (l)
3 LOAD_CONST 0 (None)
6 LOAD_CONST 0 (None)
9 BUILD_SLICE 2
12 BINARY_SUBSCR
13 RETURN_VALUE
As you can see, they're exactly the same. 1 nothing (two LOAD_CONSTS) is loaded and applied for both the start and stop values of the build slice (BUILD_SLICE). NONE is the default value described in the slice document in Standard Type hierarchy:
[Special read-only attributes: start is the lower bound; stop is the upper bound; step is the step value; each is None if omitted. These attributes can have any type.
]With [:], it has fewer keystrokes.
What is actually interesting is that in Python 2.x, the generated byte code is different and may be slightly more efficient because l [:] has fewer commands:
>>> def foo():
... l[:]
...
>>> dis(foo)
2 0 LOAD_GLOBAL 0 (l)
3 SLICE+0
4 POP_TOP
5 LOAD_CONST 0 (None)
8 RETURN_VALUE
For l [::] :
>>> def foo2():
... l[::]
...
>>> dis(foo2)
2 0 LOAD_GLOBAL 0 (l)
3 LOAD_CONST 0 (None)
6 LOAD_CONST 0 (None)
9 LOAD_CONST 0 (None)
12 BUILD_SLICE 3
15 BINARY_SUBSCR
16 POP_TOP
17 LOAD_CONST 0 (None)
20 RETURN_VALUE
Even if I don't schedule these (I won't, the difference should be small) it seems that l [:] might be a little bit better because it requires fewer instructions.
This similarity is certainly not in the list; It applies to all sequences in Python:
# Note: the Bytecode class exists in Py > 3.4
>>> from dis import Bytecode
>>>
>>> Bytecode('(1, 2, 3)[:]').dis() == Bytecode('(1, 2, 3)[::]').dis()
True
>>> Bytecode('"string"[:]').dis() == Bytecode('"string"[::]').dis()
True
The same is true for others.
conclusion